<?php

/*
    URL class

        Author:     Alfredo Mungo
        License:    LGPL
        Copyright:  2012
        
        RFC: http://tools.ietf.org/html/rfc1738
*/

/*
  URL class
  
  Manages URLs by automatically splitting them into their parts and decoding percent-encoded characters.
  
  Constructors:
    URL(url)
  
  Static Methods:
    N/A
  
  Methods:
    getScheme()
    getHost()
    getPort()
    getUser()
    getPassword()
    getFragment()
    getPath()
    hasParam(parm)
    getOriginal()
    getQuery(parm)
    getLongQuery()
*/
class URL {

    private $original;
    protected $data;
    protected $query;
    protected $lquery; //long query
    
    /*
        Creates a new instance of the class.
        
        Arguments:
            $url: the string representation of the URL.
            $decode: true if the instance must return URL decoded data, false if not.
    */
    function __construct($url, $decode = true) {
      $this->data = parse_url($url);
      $this->original = $url;
      
      if($decode) {
          $this->data['path'] = isset($this->data['path'])? urldecode($this->data['path']) : null;
          $this->data['user'] = isset($this->data['user'])? urldecode($this->data['user']) : null;
          $this->data['host'] = isset($this->data['host'])? urldecode($this->data['host']) : null;
          $this->data['pass'] = isset($this->data['pass'])? urldecode($this->data['pass']) : null;
          $this->data['fragment'] = isset($this->data['fragment'])? urldecode($this->data['fragment']) : null;
      }
      
      $this->data['port'] = isset($this->data['port'])? $this->data['port'] : 80;
      
      $this->query = array();
      
      if(isset($this->data['query']))
      {
        $this->lquery = $this->data['query'];
        
        $query = explode('&', $this->data['query']);
        
        foreach ($query as $parm)
        {
          $p = explode('=', $parm);
          
          if($decode)
            $p[0] = urldecode($p[0]);
          
          if(count($p) == 2) {
            if($decode)
                $p[1] = urldecode($p[1]);
          }
          else
            $p[] = '';
          
          $this->query[$p[0]] = $p[1];
        }
      }
    }
    
    private function getData($idx) {
      return isset($this->data[$idx])? $this->data[$idx]: null;
    }
    
    // GET methods
    /*
      Return the requested URL part. If the requested part is not present in the URL, null is returned.
    */
    
    public function getScheme() { return $this->getData('scheme'); }
    public function getHost() { return $this->getData('host'); }
    public function getPort() { return $this->getData('port'); }
    public function getUser() { return $this->getData('user'); }
    public function getPassword() { return $this->getData('pass'); }
    public function getFragment() { return $this->getData('fragment'); }
    public function getPath() { return $this->getData('path'); }
    public function hasParam($parm) { return isset($this->query[$parm]); }
    public function getOriginal() { return $this->original; }
    
    /*
      Return the query (GET parameters).
      
      Arguments:
        string $parm: the GET parameter to retrieve, or null to retrieve an associative array filled with all the GET parameters.
                      If no parameters are present, an empty array is returned.
    */
    public function getQuery($parm = null) { return $parm == null? $this->query: (isset($this->query[$parm])? $this->query[$parm]: null); }
    
    /*
        Return the query string.
    */
    public function getLongQuery() { return $this->lquery; }
    
    // GET methods - end
    
    /*
        Visit the specified URL (if headers have not yet been sent).
        
        Arguments:
            string $url: the URL to visit
            bool $immediately: true to immediately visit the URL (exit the script).
        
        Return:
            true on success, false on failure.
            If $immediately = true, the function will never return.
    */
    public static function visitURL($url, $immediately = true) {
        if(!headers_sent()) {
            if(is_string($url)) {
                header("Location: " . $url);
                if($immediately)
                    exit;
                else
                    return true;
            } else return false;
        } else return false;
    }
    
    public function visit($immediately = true) {
        self::visitURL($this->original, $immediately);
    }
    
    // Return a normalized version of the URL
    public static function normalizeURL($url, $remove_query = true, $remove_fragment = true) {
        $url = new URL(strtolower($url), false);

        $res = $url->getScheme() . '://' . $url->getHost() . ($url->getPort() != 80? ':' . $url->getPort(): '');
        
        $path = explode('/', $url->getPath());
        foreach($path as $k => $v) // Remove . and .. and URLencode path
            if($v == '.')
                unset($path[$k]);
            elseif ($v == '..') {
                for($i = $k - 1; $i > 0; $i--)
                    if(isset($path[$i])) {
                        unset($path[$i]);
                        break;
                    }
                unset($path[$k]);
            } else
                $path[$k] = rawurlencode($path[$k]);
        
        $res .= implode('/', $path);

        if(!$remove_query) {
            $q = $url->getQuery();
            
            if(count($q) > 0) {
            
                $res .= '?';
                
                foreach($q as $k => $v) {
                    if($k !== '')
                        $res .= rawurlencode($k) . '=' . rawurlencode($v) . '&';
                }
                
                $res = substr($res, 0, strlen($res) - 1);
            }
        }
        
        if(!$remove_fragment && $url->getFragment() !== null) {
            $res .= '#' . rawurlencode($url->getFragment());
        }
        return $res;
    }
    
    public function normalize($remove_query = true, $remove_fragment = true) {
        return self::normalizeURL($this->original, $remove_query, $remove_fragment);
    }
}

?>